package com.xuecheng.media.service.jobhandler;

import com.xuecheng.base.utils.Mp4VideoUtil;
import com.xuecheng.media.model.po.MediaProcess;
import com.xuecheng.media.service.MediaFileProcessService;
import com.xuecheng.media.service.MediaFileService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 视频处理任务
 */
@Slf4j
@Component
public class VideoTask {

    @Autowired
    MediaFileProcessService mediaFileProcessService;

    @Autowired
    MediaFileService mediaFileService;

    @Value("${videoprocess.ffmpegpath}")
    private String ffmpegpath;

    /**
     * 视频处理任务
     */
    @XxlJob("videoJobHandler")
    public void videoJobHandler() throws Exception {

        // 分片参数
        //分片序号
        int shardIndex = XxlJobHelper.getShardIndex();
        //分片总数
        int shardTotal = XxlJobHelper.getShardTotal();

        //确定CPU的核心数
        int processors = Runtime.getRuntime().availableProcessors();
        //查询待处理任务
        List<MediaProcess> mediaProcessList = mediaFileProcessService.getMediaProcessList(shardIndex, shardTotal, processors);
        //任务数量
        int size = mediaProcessList.size();
        log.debug("获取到的任务数" + size);
        if (size <= 0) {
            return;
        }
        //开启线程池，多线程执行任务
        ExecutorService executorService = Executors.newFixedThreadPool(size);
        //创建计数器
        CountDownLatch countDownLatch = new CountDownLatch(size);
        mediaProcessList.forEach(mediaProcess -> {
            //将任务加入到线程池
            executorService.execute(() -> {
                try {
                    //任务ID
                    Long taskId = mediaProcess.getId();
                    //文件id == fileMd5
                    String fileId = mediaProcess.getFileId();
                    //获得需要执行的任务
                    //开启任务，获得锁
                    boolean b = mediaFileProcessService.startTask(taskId);
                    if (!b) {
                        log.debug("获取任务锁失败，任务id:{}", taskId);
                        return;
                    }
                    //获取任务成功，开始执行视频转码
                    //需要先从minio从下载视频
                    //获得桶
                    String bucket = mediaProcess.getBucket();
                    //获得objectName == 文件存储路径
                    String objectName = mediaProcess.getFilePath();
                    //调用方法获得从minio下载的临时文件
                    File file = mediaFileService.downloadFileFromMinIo(bucket, objectName);
                    if (file == null) {
                        log.error("下载任务失败,任务id:{},bucket:{},objectName:{}", taskId, bucket, objectName);
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, "下载任务失败");
                        return;
                    }
                    //下载成功开始转码
                    //ffmpeg的路径
                    String ffmpeg_path = ffmpegpath;//ffmpeg的安装位置
                    //源avi视频的路径
                    String video_path = file.getAbsolutePath();
                    //转换后mp4文件的名称
                    String mp4_name = fileId + ".mp4";
                    //创建临时文件，作为转化后的文件
                    File mp4file = null;
                    try {
                        mp4file = File.createTempFile("minioTemp", ".mp4");
                    } catch (IOException e) {
                        log.error("创建临时文件异常,{}", e.getMessage());
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, "创建临时文件异常");
                        return;
                    }
                    //转换后mp4文件的路径
                    String mp4_path = mp4file.getAbsolutePath();
                    //创建工具类对象
                    Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpeg_path, video_path, mp4_name, mp4_path);
                    //开始视频转换，成功将返回success,失败则返回失败原因
                    String result = videoUtil.generateMp4();
                    if (!result.equals("success")) {
                        log.error("视频转码失败，原因:{},bucket:{},objectName:{}", result, bucket, objectName);
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, result);
                        return;
                    }
                    String mp4ObjectName = getFilePathByMd5(fileId,".mp4");
                    //视频转码成功，开始上传到minio
                    boolean b1 = mediaFileService.addMediaFilesToMinIo(mp4file.getAbsolutePath(), "video/mp4", bucket, mp4ObjectName);
                    if (!b1) {
                        log.error("视频上传到minio失败,taskId:{}", taskId);
                        mediaFileProcessService.saveProcessFinishStatus(taskId, "3", fileId, null, "视频上传到minio失败");
                        return;
                    }
                    //mp4文件的url
                    String url = "/" + bucket + "/" + mp4ObjectName;
                    mediaFileProcessService.saveProcessFinishStatus(taskId, "2", fileId, url, null);

                } finally {
                    //计数器减1
                    countDownLatch.countDown();
                }
            });
        });

        //阻塞,指定最大限度的等待时间，阻塞最多等待一定时间后结束
        countDownLatch.await(30, TimeUnit.MINUTES);
    }

    /**
     * 得到合并后的文件地址
     *
     * @param fileMd5 文件md5
     * @param fileExt 文件扩展名
     * @return 合并后的文件地址---url
     */
    private String getFilePathByMd5(String fileMd5, String fileExt) {
        return fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5 + fileExt;
    }
}
